#!/usr/bin/env python  
# -*- coding: utf-8 -*-  

#
#   this is a rewrite of the SQLite python filesystem to use
#   sqlalchemy so I can easily use other SQL databases for the backend
#
#
#   INCOMPLETE, NOT WORKING




import errno  
import fuse  
import stat  
import time  
import os, math, hashlib, zlib
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool
  
fuse.fuse_python_api = (0, 2)  
Base = declarative_base()

class files(Base):
    __tablename__ = 'files'

    f_id = Column(Integer, primary_key=True)
    filename = Column(String)
    size = Column(Integer)
    
    def __init__(self, filename, size):
        self.filename = filename
        self.size = size

    def __repr(self):
        return "<files('%s','%s')>"%(self.filename, str(size))

class dirs(Base):
    __tablename__ = 'dirs'

    dirname = Column(String, primary_key = True)
    
    def __init__(self, dirname):
        self.dirname = dirname

    def __repr(self):
        return "<dirs('%s')>"%(self.dirname)



  
class MyFS(fuse.Fuse):  
    def __init__(self, *args, **kw):  
        fuse.Fuse.__init__(self, *args, **kw)  
        self.stuff=dict()
        self.chunkSize = 1024
        self.fd=0
        self.chunkPath = "/mnt/sdb9/TEST/chunks"
        self.metadb = "sqlite://:memory:"
        if os.path.isdir(self.chunkPath) == False:
            os.mkdir(self.chunkPath)
            for i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f"]:
                os.mkdir("%s/%s"%(self.chunkPath, str(i)))
            os.mkdir("%s/%s"%(self.chunkPath, 'left'))
        self.db = create_engine("sqlite:///:memory:", echo = True, connect_args={'check_same_thread':False}, poolclass=StaticPool)
        Base.metadata.create_all(self.db)
        self.Session = sessionmaker(bind = self.db)
        self.dbSession = self.Session()
        d = dirs('/')
        self.dbSession.add(d)
        self.dbSession.commit()

    def chmod(self, path, mode):
        pass
    def chown(self, path, uid, gid):
        pass
    def fsync(self, path, isFsyncFile):
        pass
    def rename(self,oldPath,newPath ):
        self.db.execute( self.files.update().where(self.files.c.filename==oldPath).values(filename=newPath) )
        
    def getattr(self, path):  
        rows = self.dbSession.query(dirs.dirname).filter_by(dirname=path).count()
        if rows == 1:
            st = fuse.Stat()  
            st.st_mode = stat.S_IFDIR | 0777
            st.st_nlink = 2
            st.st_atime = int(time.time())
            st.st_mtime = int(time.time())
            st.st_ctime = int(time.time())
            return st
        rows = self.dbSession.query(files.filename).filter_by(filename=path).count()
        if rows == 1:
            st = fuse.Stat()  
            st.st_mode = stat.S_IFREG | 0777
            st.st_nlink = 1
            st.st_atime = int(time.time())
            st.st_mtime = int(time.time())
            st.st_ctime = int(time.time())
            st.st_size = rows[0]['size']
            return st
        return -errno.ENOENT

    def readdir(self, path, offset):  
        send=['.','..']
        if path == "/":
            q = self.dbSession.query(files.filename).from_statement("select filename from files where filename like :name and filename not like :badname").params(name="%s%%"%path, badname="%s%%/%%"%path)
        else:
            q = self.dbSession.query(files.filename).from_statement("select filename from files where filename like :name and filename not like :badname").params(name="%s/%%"%path, badname="%s/%%/%%"%path)
        rows = q.all()
        for row in rows:
            tmp=""
            tmp = row['filename']
            if path == "/":
                tmp = tmp[len(path):]
            else:
                tmp = tmp[len(path)+1:]
            send.append(str(tmp))
        if(path == "/"):
            q = self.dbSession.query(dirs.dirname).from_statement("select dirname from dirs where dirname like :name and dirname not like :badname").params(name="%s%%"%path, badname="%s%%/%%"%path)
        else:
            q = self.dbSession.query(dirs.dirname).from_statement("select dirname from dirs where dirname like :name and dirname not like :badname").params(name="%s/%%"%path, badname="%s/%%/%%"%path)
        rows = q.all()
        for row in rows:
            tmp=""
            tmp = row[0]
            if str(tmp) == str(path):
                continue
            if(path == "/"):
                tmp = tmp[len(path):]
            else:
                tmp = tmp[len(path)+1:]
            if tmp.find("/") > 0:
                continue
            send.append(str(tmp))
        for x in send:
            yield fuse.Direntry(x)

    def mkdir(self, path, mode):
        d = dirs(path)
        self.dbSession.add(d)
        self.dbSession.commit()

    def rmdir(self, path):
        q = self.dbSession.query(dirs.dirname).from_statement("select dirname from dirs where dirname like :name").params(name="%s/%%"%path)
        rows = q.all()
        if len(rows) > 0:
            return -errno.ENOTEMPTY
        q = self.dbSession.query(files.filename).from_statement("select filename from files where filename like :name").params(name="%s/%%"%path)
        rows = q.all()
        if len(rows) > 0:
            return -errno.ENOTEMPTY
        del q
        del rows
        self.dbSession.commit()
        self.dbSession.new
        print "--------START-----------"
        self.db.execute("delete from dirs where dirname = ?", path)
        print "--------END-----------"
        self.dbSession.commit()
        self.dbSession.new

    def mknod(self, path, mode, dev):
#        c.execute("insert into files VALUES(null, ?, 0)",(path,))
#        conn.commit()
        pass
    def unlink(self, path):
#        c.execute("select * from files where filename = ?",(path,))
#        s = c.fetchall()
#        if len(s) == 1:
#            c.execute("delete from files where f_id = ?",(s[0]['f_id'],))
#            c.execute("select sum from data where f_id = ?", (s[0]['f_id'],))
#            rows = c.fetchall()
#            c.execute("delete from data where f_id = ?",(s[0]['f_id'],))
        pass
        
    def open(self, path, flags):
        return 0

    def utimens(self, path, tmp, tmp2):
        return 0

    def write(self, path, data, offset):
        return 0
        ret = len(data)
        conn = sqlite3.connect(self.metadb)
        conn.row_factory = sqlite3.Row
        conn.text_factory = str
        c = conn.cursor()
        c.execute("pragma synchronous=off")
        c.execute("pragma journal_mode=off")
        c.execute("pragma cache_size=800000")
        c.execute("pragma temp_store=2")
        c.execute("pragma PAGE_SIZE=4096")

        c.execute("select * from files where filename = ?",(path,))
        s = c.fetchall()
        if len(s) == 1:
            f_id = s[0]['f_id']
            size = s[0]['size']
            left = size % self.chunkSize
#            print "Start Write"
            Wtime = int(time.time())
            if offset != size:
                return -errno.EPERM
            if left > 0:
                tf = open("%s/%s/%s"%(self.chunkPath, 'left', str(f_id)), "r")
                data = "%s%s"%(tf.read(left), data)
                tf.close()
                os.remove("%s/%s/%s"%(self.chunkPath, 'left', str(f_id)))
            while len(data) > 0:
                bs = self.chunkSize
                if len(data) >= self.chunkSize:
                    s = hashlib.sha1(data[:bs]).hexdigest()
                    c.execute("insert into data VALUES(?, ?, ?, ?)", (f_id, offset-left, (offset-left)+len(data[:bs]), s, ))
                    if os.path.isfile("%s/%s/%s"%(self.chunkPath, s[:1], s)) == False:
                        tf = open("%s/%s/%s"%(self.chunkPath, s[:1], s), "w")
                        tf.write(data[:bs])
                        tf.close()
                else:
                    bs = len(data)
                    tf = open("%s/%s/%s"%(self.chunkPath, 'left', str(f_id)), "w")
                    tf.write(data[:bs])
                    tf.close()
                data = data[bs:]
                offset=offset+bs
            if offset-left > size:
                c.execute("update files set size = ? where f_id = ?",(offset-left, f_id,))    
        conn.commit()
        if (int(time.time())-Wtime) > 2:
            print "Write Took %d Total write size: %d"%(int(time.time())-Wtime, ret)
        return ret

    def read(self, path, size, offset):
        return 0
        conn = sqlite3.connect(self.metadb)
        conn.row_factory = sqlite3.Row
        conn.text_factory = str
        c = conn.cursor()
        c.execute("pragma synchronous=off")
        c.execute("select * from files where filename = ?",(path,))
        s = c.fetchall()
        c_data = ""
        if len(s) == 1:
            f_id = s[0]['f_id']
            tsize = s[0]['size']
            left = tsize%self.chunkSize
            start = 0
            c.execute("select f_id, start, end, sum from data where f_id = ? and ? >= start and ? < end order by start", (f_id, offset+size, offset  ))
            rows = c.fetchall()
            print "Got %d Rows"%len(rows)
            if len(rows) != 0:
                start = rows[0]['start']
                for row in rows:
                    tf = open("%s/%s/%s"%(self.chunkPath, row['sum'][:1], row['sum']), "r")
                    c_data += tf.read(row['end'] - row['start'])
                    tf.close()
                    if len(c_data) > size:
                        break
            if (offset+size) > tsize-left:
                if start == 0 and len(rows) == 0:
                    start = tsize-left
                tf = open("%s/%s/%s"%(self.chunkPath, 'left', f_id), "r")
                c_data += tf.read(left)
                tf.close()
            print "Reading tsize: %d left :%d\nsize %d\noffset %d\nstart %d\nTotal Length %d\nReturn Length %d"%(tsize, left, size, offset, start, len(c_data), len(c_data[(offset-start):(offset-start)+size]))

            return c_data[(offset-start):(offset-start)+size]
        return -errno.ENOENT

    def truncate(self, path, length):
        pass
        
        
        
        
        
        


        
        
      
if __name__ == '__main__':  
    fs = MyFS()  
    fs.parse(errex=1)  
    fs.main()  
